﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Newtonsoft.Json;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Common.MQ
{
     public class MQHelper
    {
        private static IConnectionFactory factory = null;
        private static object lockobj = new object();
        private static IConnection? con = null;
        private static object lockobj1 = new object();
        private static IConfigurationRoot GetConfig()
        {
            var _builder = new ConfigurationBuilder();
            var config = _builder.Add(new JsonConfigurationSource { Path = "appsettings.json", Optional = false, ReloadOnChange = true }).Build();
            return config;
        }

        private static void GetFactory()
        {
            if(factory == null )
            {
                lock (lockobj)
                {
                    if(factory == null)
                    {
                        var config = GetConfig().GetSection("RabbitMqConfig");
                        factory = new ConnectionFactory()
                        {
                            HostName = config.GetValue<string>("HostName"),
                            Port = config.GetValue<int>("Port"),
                            UserName = config.GetValue<string>("UserName"),
                            Password = config.GetValue<string>("Password"),
                        };
                    }
                }
            }
        }
        private static void GetConnection()
        {
            GetFactory();
            if(con == null||!con.IsOpen)
            {
                lock (lockobj1)
                {
                    if(con == null || !con.IsOpen)
                    {
                        con = factory.CreateConnection();
                    } 
                }
            }
        }
        private static IModel GetChannel((string queue, bool durable, bool exclusive, bool autoDelete, IDictionary<string, object>? arguments) QueueConfig)
        {
            GetConnection();
            IModel channel= con.CreateModel();
            channel.QueueDeclare(
              queue: QueueConfig.queue,//消息队列名称
              durable: QueueConfig.durable,//是否持久化,true持久化,队列会保存磁盘,服务器重启时可以保证不丢失相关信息。
              exclusive: QueueConfig.exclusive,//是否排他,true排他的,如果一个队列声明为排他队列,该队列仅对首次声明它的连接可见,并在连接断开时自动删除.
              autoDelete: QueueConfig.autoDelete,//是否自动删除。true是自动删除。自动删除的前提是：致少有一个消费者连接到这个队列，之后所有与这个队列连接的消费者都断开时,才会自动删除.
              arguments: QueueConfig.arguments //设置队列的一些其它参数
               );
            return channel;
        }
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="message">发送的信息</param>
        /// <param name="QueueConfig">队列配置</param>
        public static void ProduceMessage(string message,(string queue,bool durable,bool exclusive,bool autoDelete,IDictionary<string,object>? arguments) QueueConfig,string exchange="", IBasicProperties? basicProperties=null)
        {
            IModel channel = GetChannel(QueueConfig);
            byte[] body = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish(exchange, QueueConfig.queue, null, body);
            channel.Dispose();
        }

        public static void ProduceMessage<T>(T message, (string queue, bool durable, bool exclusive, bool autoDelete, IDictionary<string, object>? arguments) QueueConfig, string exchange = "", IBasicProperties? basicProperties = null)
            where T : class
        {
            IModel channel = GetChannel(QueueConfig);
            byte[] body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));
            channel.BasicPublish(exchange, QueueConfig.queue, null, body);
            channel.Dispose();
        }

        public static void ProduceMessage<T>(IEnumerable<T> messages, (string queue, bool durable, bool exclusive, bool autoDelete, IDictionary<string, object>? arguments) QueueConfig, string exchange = "", IBasicProperties? basicProperties = null)
            where T : class
        {
            IModel channel = GetChannel(QueueConfig);
            foreach (var message in messages)
            {
                byte[] body = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));
                channel.BasicPublish(exchange, QueueConfig.queue, null, body);
            }
            channel.Dispose();
        }
        /// <summary>
        /// 生产消息
        /// </summary>
        /// <param name="messageList">消息list</param>
        /// <param name="QueueConfig">队列配置</param>
        public static void ProduceMessage(IEnumerable<string> messageList, (string queue, bool durable, bool exclusive, bool autoDelete, IDictionary<string, object>? arguments) QueueConfig, string exchange = "", IBasicProperties? basicProperties = null)
        {
            IModel channel = GetChannel(QueueConfig);
            foreach (string message in messageList)
            {
                byte[] body = Encoding.UTF8.GetBytes(message);
                channel.BasicPublish(QueueConfig.queue, QueueConfig.queue, null, body);
            }
            channel.Dispose();
        }
        public static void CustomerMessage(Action<string> action, (string queue, bool durable, bool exclusive, bool autoDelete, IDictionary<string, object>? arguments) QueueConfig)
        {
            IModel channel = GetChannel(QueueConfig);
            channel.BasicQos(0, 1, false);
            var consumer = new EventingBasicConsumer(channel);
            consumer.Received += (model, ea) =>
            {
                var open = channel.IsOpen;
                string message = Encoding.UTF8.GetString(ea.Body.ToArray());//接收到的消息
                action(message);
                open = channel.IsOpen;
                channel.BasicAck(ea.DeliveryTag, false);
                channel.Dispose();
            };
            channel.BasicConsume(QueueConfig.queue, false, consumer);
            
        }

        public static void CustomerMessage<T>(Action<T> action,(string queue, bool durable, bool exclusive, bool autoDelete, IDictionary<string, object>? arguments) QueueConfig)
            where T : class
        {
            IModel channel = GetChannel(QueueConfig);
            channel.BasicQos(0, 1, false);
            var consumer = new EventingBasicConsumer(channel);
            consumer.Received += (model, ea) =>
            {
                var open=channel.IsOpen;
                string message = Encoding.UTF8.GetString(ea.Body.ToArray());//接收到的消息
                T item = JsonConvert.DeserializeObject<T>(message);
                action(item);
                open = channel.IsOpen;
                channel.BasicAck(ea.DeliveryTag, false);
                channel.Dispose();
            };
            channel.BasicConsume(QueueConfig.queue, false, consumer); 
        }

    }
}
